#pragma once
/*
模块说明：
提供两个客户端（创建两个socket fd）负责连接服务中心订阅主题或者创建主题或者接收
来自服务中心的主题内容，一个客户端负责发送主题内容发布请求

主题请求中的正文中除了主题内容发布请求，其它的请求应该是这样的json字符串
:{"topic": "xxx","toper_type" : 0}即只有主题字段和主题操作字段这两个字段
而主题内容发布请求：{"topic": "xxx","toper_type" : 0,"topic_msg":"xxxxxxxx"}
*/

#include "../Net.hpp"
#include "../Unit.hpp"
#include "../Dispatch.hpp"
#include "requestor.hpp"

namespace Topic_Manager
{
    class TopicManager
    {
    public:
        using TManagerPtr = std::shared_ptr<TopicManager>;
        // 该回调函数负责处理客户端订阅了某个主题时，当这个主题有内容发布时，服务中心把内容推送给
        // 该客户端时的回调函数，一个主题对应一个回调处理函数
        using DealTopicBack = std::function<void(const std::string &topicname, const std::string &msg)>;
        TopicManager(const Client::Resquestor::RPtr &Re) : _requestor(Re)
        {
        }

        // 提供接口给客户端使用
        // 创建一个主题--发送创建主题请求同步等待回应
        bool create(const Abstract::BaseConnection::ConPtr &con, const std::string &topicname)
        {
            DLOG("进入创建topic请求模块");
            if (con.get() == nullptr)
            {
                DLOG("con为空");
            }
            return MakeTopicRequest(con, topicname, TopicOperType::Topic_Create);
        }
        bool remove(const Abstract::BaseConnection::ConPtr &con, const std::string &topicname)
        {
            return MakeTopicRequest(con, topicname, TopicOperType::Topic_Delete);
        }
        // 订阅一个主题同时注册一个对应的处理接收该主题的内容时的回调函数
        bool subscribe(const Abstract::BaseConnection::ConPtr &con, const std::string &topicname, const DealTopicBack &Cb)
        {
            // 1 先设置回调函数再发起订阅请求，防止主线程在订阅成功后马上有主题内容推送过来
            // 然后监听线程调用回调函数去处理，但是这个时候对应的回调函数是还没设置的
            SetSubBack(topicname, Cb);
            bool ret = MakeTopicRequest(con, topicname, TopicOperType::Topic_SubScribe);
            if (ret == false) // 如果订阅失败删除对应关系
            {
                DelSubBack(topicname);
            }
            return ret;
        }
        bool cancel(const Abstract::BaseConnection::ConPtr &con, const std::string &topicname)
        {
            return MakeTopicRequest(con, topicname, TopicOperType::Topic_Cancle_Sub);
        }
        // 推送主题的内容
        bool publish(const Abstract::BaseConnection::ConPtr &con, const std::string &topicname, const std::string &msg)
        {
            return MakeTopicRequest(con, topicname, TopicOperType::Topic_SendMesg, msg);
        }
        // 接收到主题请求时的处理函数即服务中心推送过来的主题内容--给注册到订阅客户端的dispatch模块
        bool DelpublishComment(const Abstract::BaseConnection::ConPtr &con, const Abstract::BaseMessage::MesaPtr MPtr)
        {
            auto TopicRequestPtr = std::dynamic_pointer_cast<Message::TopicRequest>(MPtr);
            std::string topicname = TopicRequestPtr->GetTopic();
            if (_Topic_Backs.count(topicname))
            {
                // 如果有该主题内容对应的回调处理函数
                _Topic_Backs[topicname](topicname, TopicRequestPtr->GetTopicMsg());
                return true;
            }
            ELOG("没有该主题:%s的内容的对应的回调处理函数", topicname.c_str());
            return false;
        }

    private:
        void SetSubBack(const std::string &topicname, const DealTopicBack &Cb)
        {
            // 防止多个监听线程同时添加
            std::unique_lock<std::mutex> lock(_mutex);
            if (_Topic_Backs.count(topicname) <= 0)
            {
                _Topic_Backs.insert({topicname, Cb});
            }
        }
        void DelSubBack(const std::string &topicname)
        {
            // 防止多个监听线程同时添加
            std::unique_lock<std::mutex> lock(_mutex);
            if (_Topic_Backs.count(topicname) > 0)
            {
                _Topic_Backs.erase(topicname);
            }
        }

    private:
        bool MakeTopicRequest(const Abstract::BaseConnection::ConPtr &con, const std::string &topicname, TopicOperType Type, const std::string &msg = "")
        {
            // 1 组织消息
            DLOG("创建topicrequest模块");
            Message::TopicRequest::TopicPtr CreatePtr = Message::BuildMessage::build<Message::TopicRequest>();
            if (CreatePtr.get() == nullptr)
            {
                DLOG("创建topicrequest消息失败");
                return false;
            }
            CreatePtr->SetUuid(UUID::uuid());
            CreatePtr->SetMtype(Rs_RqType::TOPIC_Req);
            CreatePtr->SetTopic(topicname);
            CreatePtr->SetTopOper(Type);
            // 如果该主题请求的操作是主题内容发布
            if (Type == TopicOperType::Topic_SendMesg)
            {
                CreatePtr->SetTopicMsg(msg);
            }
            // 2 发送消息主线程同步等待回应监听线程接收到回应
            Abstract::BaseMessage::MesaPtr MPtr;
            DLOG("创建topicrequest模块");
            if (con.get() == nullptr)
            {
                DLOG("con为空");
            }
            bool ret = _requestor->Send(con, CreatePtr, MPtr);
            DLOG("创建topicrequest模块");
            if (ret == false)
            {
                ELOG("发送请求失败");
                return false;
            }

            // 3 把获取的基类指针转回子类Rpcmessage指针指针
            auto Topic_ResPtr = std::dynamic_pointer_cast<Message::TopicResponceJson>(MPtr);
            if (Topic_ResPtr.get() == nullptr)
            {
                ELOG("Topic创建响应的基类指针向下转化失败");
                return false;
            }
            if (Topic_ResPtr->Getcode() != ResCode::ResCode_Ok)
            {
                ELOG("Topic创建请求的回应结果出错:%s", GetResCodeStr(Topic_ResPtr->Getcode()).c_str());
                return false;
            }
            return true;
        }

    private:
        std::mutex _mutex;
        std::unordered_map<std::string, DealTopicBack> _Topic_Backs; // 存储接收到主题内容和对应的回调函数的映射关系
        Client::Resquestor::RPtr _requestor;                         // 消息发送和接收模块
    };

}